﻿//-----------------------------------------------------------------------
// <copyright file="EntityTuple.cs" company="tuliprules.org">
//     Copyright (c) 2010 tuplelister, tuliprules.org.
// </copyright>
// <summary>This is the EntityTuple class.</summary>
//-----------------------------------------------------------------------

namespace Tulip.Engine.Tuples
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using Exceptions;

    /// <summary>
    /// Base class for entity tuples.
    /// </summary>
    public class EntityTuple : Tuple
    {
        /// <summary>
        /// Flag indicates if this tuple is entity tuple.
        /// </summary>
        public bool IsEntity = true;

        /// <summary>
        /// Maintains a list of member names that must be re-cached.
        /// </summary>
        protected HashSet<string> cacheAddList = new HashSet<string>();

        /// <summary>
        /// Maintains a list of member names that must be removed from the cache.
        /// </summary>
        protected HashSet<string> cacheRemovalList = new HashSet<string>();

        /// <summary>
        /// The fact associated with the entity tuple.
        /// </summary>
        private object fact;

        /// <summary>
        /// Stores a number that indicates id of assertion.   If the Identifiers are 
        /// set in incremental order of assertion, they can be used as timestamps.
        /// </summary>
        private long id;

        /// <summary>
        /// Gets or sets the fact associated with the entity tuple.
        /// </summary>
        public virtual object Fact
        {
            get
            {
                return this.fact;
            }

            set
            {
                // The Tuple is immutable
                if (this.fact == null)
                {
                    this.fact = value;
                    return;
                }

                TupleStoreExceptionHelper.ThrowTupleStoreExTupleImmutable();
            }
        }

        /// <summary>
        /// Gets or sets the ID of the entity tuple.
        /// </summary>
        public long Id
        {
            get
            {
                return this.id;
            }

            set
            {
                if (TraceIDs)
                {
                    if (this.Identifiers == null)
                    {
                        this.Identifiers = new List<long>(16);
                    }

                    this.Identifiers.Add(value);
                }

                this.id = value;
            }
        }

        /// <summary>
        /// Updates the cache using the Re-Cache list.
        /// </summary>
        public void UpdateCache()
        {
            foreach (var memberName in this.cacheAddList)
            {
                this.SetCache(memberName);
            }

            this.cacheAddList.Clear();
        }

        /// <summary>
        /// Updates the fact using the De-Cache list.
        /// </summary>
        public virtual void UpdateFact()
        {
            // This is virtual rather than abstract in order to avoid need to override when not needed
        }

        /// <summary>
        /// Returns the hashcode of the contained fact.
        /// </summary>
        /// <returns>Hashcode for the contained fact.</returns>
        public override int GetHashCode()
        {
            return this.fact == null ? base.GetHashCode() : this.fact.GetHashCode();
        }

        /// <summary>
        /// Returns the type of the fact associated with the entity tuple.
        /// </summary>
        /// <returns>The type of the fact associated with the entity tuple.</returns>
        public Type FactType()
        {
            return this.fact == null ? this.GetType() : this.fact.GetType();
        }

        /// <summary>
        /// Disposes the tuple.
        /// </summary>
        public override void Clear()
        {
            base.Clear();
            this.fact = null;
        }

        /// <summary>
        /// Indicates if the current object supports the INotifyPropertyChanged interface.
        /// </summary>
        /// <param name="value">The object to be tested.</param>
        /// <returns>True if the current object supports the INotifyPropertyChanged interface, otherwise false.</returns>
        protected static bool IsINotifyPropertyChanged(object value)
        {
            return value is INotifyPropertyChanged;
        }

        /// <summary>
        /// Add the member name to the Re-Cache list.   Test for, and
        /// eliminate any duplicates.
        /// </summary>
        /// <param name="memberName">Name of member to be re-cached.</param>
        protected void AddToCacheAddList(string memberName)
        {
            if (this.cacheAddList.Contains(memberName))
            {
                return;
            }

            this.cacheAddList.Add(memberName);
        }

        /// <summary>
        /// Add the member name to the De-Cache list.   Test for, and
        /// eliminate any duplicates.
        /// </summary>
        /// <param name="memberName">Name of member to be de-cacheded (i.e., copied back to Fact.</param>
        protected void AddToCacheRemovalList(string memberName)
        {
            if (this.cacheRemovalList.Contains(memberName))
            {
                return;
            }

            this.cacheRemovalList.Add(memberName);
        }

        /// <summary>
        /// Sets all cacheable values in the cache.
        /// </summary>
        protected virtual void SetCache()
        {
            // This is virtual rather than abstract in order to avoid need to override when not needed
        }

        /// <summary>
        /// Sets a single value in the cache.
        /// </summary>
        /// <param name="memberName">Name of the member whose value is to be cached.</param>
        protected virtual void SetCache(string memberName)
        {
            // This is virtual rather than abstract in order to avoid need to override when not needed
        }

        /// <summary>
        /// NewRuleSet handler.   Handles property change notifications.
        /// </summary>
        /// <param name="sender">Fact object.</param>
        /// <param name="e">NewRuleSet args.</param>
        protected void BoundFactPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (!string.IsNullOrEmpty(e.PropertyName))
            {
                this.AddToCacheAddList(e.PropertyName);
            }
        }
    }
}
